package com.icloud.article.boot.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.icloud.article.boot.dao.ArticleDao;
import com.icloud.article.boot.dao.HotArticleDao;
import com.icloud.article.boot.dto.*;
import com.icloud.article.boot.mapper.HotArticleBrowsedMapper;
import com.icloud.article.boot.mapper.HotArticleMapper;
import com.icloud.article.boot.model.ArticleVisibleCustomer;
import com.icloud.article.boot.model.HotArticle;
import com.icloud.article.boot.model.HotArticleBrowsed;
import com.icloud.article.boot.query.ArticleQuery;
import com.icloud.article.boot.service.ArticleService;
import com.icloud.article.boot.service.ArticleVisibleCustomerService;
import com.icloud.article.boot.service.HotArticleService;
import com.icloud.article.boot.vo.AdminConsumptionVo;
import com.icloud.article.boot.vo.ArticleVo;
import com.icloud.article.boot.vo.ConsumptionVo;
import com.icloud.common.core.api.CommonResult;
import com.icloud.common.core.api.ResultCode;
import com.icloud.common.database.enums.AppScreenLocationEnum;
import com.icloud.common.database.enums.ArticleVisibleEnum;
import com.icloud.common.database.enums.PaymentStateEnum;
import com.icloud.common.database.mapper.AppCustomerMapper;
import com.icloud.common.database.mapper.CustomerWalletMapper;
import com.icloud.common.database.mapper.THotArticleOrderItemMapper;
import com.icloud.common.database.mapper.TOrderMapper;
import com.icloud.common.database.model.*;
import com.icloud.common.web.exception.ApiException;
import com.icloud.common.web.exception.Asserts;
import com.icloud.common.web.util.AuthUtil;
import com.icloud.common.web.vo.PageVO;
import com.icloud.wallet.api.dto.CustomerHotArticleDto;
import com.icloud.wallet.api.feign.CustomerOrderFeign;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 热帖service 实现类
 */

@Service
public class HotArticleServiceImpl extends ServiceImpl<HotArticleMapper, HotArticle> implements HotArticleService {
    @Autowired
    private ArticleService articleService;
    @Autowired
    private HotArticleBrowsedMapper hotArticleBrowsedMapper;
    @Autowired
    private AuthUtil authUtil;
    @Autowired
    private HotArticleDao hotArticleDao;
    @Autowired
    private ArticleVisibleCustomerService articleVisibleCustomerService;
    @Autowired
    private ArticleDao articleDao;
    @Autowired
    private Executor asyncExecutor;
    @Autowired
    private CustomerWalletMapper customerWalletMapper;
    @Autowired
    private AppCustomerMapper appCustomerMapper;
    @Autowired
    private THotArticleOrderItemMapper tHotArticleOrderItemMapper;
    @Autowired
    private TOrderMapper tOrderMapper;
    @Autowired
    private CustomerOrderFeign customerOrderFeign;

    @Override
    public ConsumptionVo consumption(ConsumptionDto dto) {
        Long loginId = authUtil.getUserId();
        Long articleId = dto.getArticleId();
        LambdaQueryWrapper<CustomerWallet> custWalletQuery = new LambdaQueryWrapper<>();
        custWalletQuery.eq(CustomerWallet::getCustomerId, loginId);
        CustomerWallet cw = customerWalletMapper.selectOne(custWalletQuery);

        ConsumptionVo vo = new ConsumptionVo();
        vo.setBcpc(cw.getBcpc());
        vo.setConsumptionPerHour(new BigDecimal("100"));
        return vo;
    }

    /**
     * 上热门
     * 热帖超时后, 再上热帖时, 浏览记录要重置
     * @param dto
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public boolean hot(ArticleHotDto dto) {
        LocalDateTime now = LocalDateTime.now();
        Long articleId = dto.getArticleId();
        Article article = articleService.getById(articleId);
        Asserts.fail(null==article, ResultCode.DATA_NOT_EXISTS);

        Integer duration = dto.getDuration();
        BigDecimal consumptionPerHour = new BigDecimal("100");
        BigDecimal amount = consumptionPerHour.multiply(new BigDecimal(duration));
        Long loginId = authUtil.getUserId();

        CustomerHotArticleDto hotArticleDto = new CustomerHotArticleDto();
        hotArticleDto.setHotTime(dto.getHotTime());
        hotArticleDto.setCreatedId(loginId);
        hotArticleDto.setDuration(duration);
        hotArticleDto.setAuthorId(article.getCreatedId());
        hotArticleDto.setArticleId(articleId);
        hotArticleDto.setAmount(amount);
        CommonResult<Boolean> hotOrderResult = customerOrderFeign.hotArticle(hotArticleDto);
        if(hotOrderResult.getCode()!=ResultCode.SUCCESS.getCode()){
            ResultCode resultCode = ResultCode.byCode(hotOrderResult.getCode());
            if(resultCode==null){
                throw new ApiException(ResultCode.SYSTEM_ERROR);
            }else {
                throw new ApiException(resultCode);
            }
        }

        LambdaQueryWrapper<HotArticle> hotQuery = new LambdaQueryWrapper<>();
        hotQuery.eq(HotArticle::getArticleId, articleId);
        HotArticle hotArticle = baseMapper.selectOne(hotQuery);

        if (null == hotArticle){
            hotArticle = new HotArticle();
            hotArticle.setArticleId(articleId);
            hotArticle.setAuthorId(article.getCreatedId());
            hotArticle.setDuration(duration);
            hotArticle.setExpireAt(now.plusHours(duration));
            baseMapper.insert(hotArticle);

            HotArticleBrowsed hab = new HotArticleBrowsed();
            hab.setArticleId(articleId);
            hab.setBrowserIds("");

            hotArticleBrowsedMapper.insert(hab);
            return true;
        }
        LocalDateTime expireAt = hotArticle.getExpireAt();
        // 热帖超时,需要重置浏览记录
        if(expireAt.isBefore(now)){
            HotArticleBrowsed reset = new HotArticleBrowsed();
            reset.setArticleId(articleId);
            reset.setBrowserIds("");
            hotArticleBrowsedMapper.updateById(reset);

            hotArticle.setDuration(duration);
            hotArticle.setExpireAt(now.plusHours(duration));
        }else{
            hotArticle.setDuration(hotArticle.getDuration()+duration);
            hotArticle.setExpireAt(hotArticle.getExpireAt().plusHours(duration));
        }
        baseMapper.updateById(hotArticle);
        return true;
    }

    /**
     * 分页查询
     * @param dto
     * @return
     */
    @Override
    public PageVO<ArticleVo> hotPage(ArticleHotPageDto dto) {
        PageVO<ArticleVo> hotPageVo = new PageVO<>();
        hotPageVo.setTotal(0L);

        Long loginId = authUtil.getUserId();
        LocalDateTime now = LocalDateTime.now();
        // 热帖固定在动态广场显示
        String appScreenLocation = AppScreenLocationEnum.SQUARE.getCode();
        ArticleQuery query = new ArticleQuery();
        query.setLoginId(loginId);
        query.setAppScreenLocation(appScreenLocation);

        IPage<HotArticle> page = Page.of(dto.getCurrent(), dto.getPageSize());

        IPage<HotArticle> hotPage = hotArticleDao.queryHotPage(page, loginId);
        List<HotArticle> hotList = hotPage.getRecords();
        if(CollectionUtil.isEmpty(hotList)){
            return hotPageVo;
        }
        List<Long> articleIdList = hotList.stream().map(HotArticle::getArticleId).collect(Collectors.toList());
        query.setArticleIdList(articleIdList);

        // 不让他(当前登录人)看,包括原创与转发
        List<ArticleVisibleCustomer> articleVisibleCustomers = articleVisibleCustomerService.list(new LambdaQueryWrapper<ArticleVisibleCustomer>()
                .eq(ArticleVisibleCustomer::getVisibleId, loginId)
                .eq(ArticleVisibleCustomer::getIsVisible, ArticleVisibleEnum.FALSE.getCode())
        );
        if(CollectionUtil.isNotEmpty(articleVisibleCustomers)){
            Set<Long> forbiddentArticleIdSet = articleVisibleCustomers.stream().map(ArticleVisibleCustomer::getArticleId).collect(Collectors.toSet());
            query.setForbiddenIds(forbiddentArticleIdSet);
        }

        List<ArticleVo> hotArticleList = articleDao.appList(query);
        if(CollectionUtil.isEmpty(hotArticleList)){
            return hotPageVo;
        }
        List<Long> hotCreatedIdList = hotArticleList.stream().map(Article::getCreatedId).collect(Collectors.toList());
        List<AppCustomer> appCustomerList = appCustomerMapper.selectBatchIds(hotCreatedIdList);
        Map<Long, AppCustomer> appCustomerMap = appCustomerList.stream().collect(Collectors.toMap(AppCustomer::getId, Function.identity()));

        for(ArticleVo avo : hotArticleList){
            AppCustomer appCustomer = appCustomerMap.get(avo.getCreatedId());
            if(appCustomer==null){
                continue;
            }
            String nickname = appCustomer.getNickname();
            String avatarUrl = appCustomer.getAvatarUrl();
            avo.setCreatedName(nickname);
            avo.setCreatedAvatarUrl(avatarUrl);
        }

        PageVO<ArticleVo> voPage = new PageVO<>();
        voPage.setTotal(page.getTotal());
        voPage.setList(hotArticleList);

        return voPage;
    }


    /**
     * 从热帖列表查看帖子,并登记浏览记录,只有这个入口才会登记浏览记录
     * @param dto
     * @return
     */
    @Override
    public ArticleVo detail(ArticleHotDetDto dto) {
        Long articleId = dto.getArticleId();
        LambdaQueryWrapper<HotArticle> hotQuery = new LambdaQueryWrapper<>();
        hotQuery.eq(HotArticle::getArticleId, articleId);
        HotArticle hotArticle = baseMapper.selectOne(hotQuery);
        Asserts.fail(null==hotArticle, ResultCode.DATA_NOT_EXISTS);

        Long loginId = authUtil.getUserId();
        String loginIdStr = String.valueOf(loginId);

        asyncExecutor.execute(()->{
            HotArticleBrowsed habExist = hotArticleBrowsedMapper.selectById(articleId);
            if(habExist==null){
                HotArticleBrowsed hab = new HotArticleBrowsed();
                hab.setArticleId(articleId);
                hab.setBrowserIds(loginIdStr);
                hotArticleBrowsedMapper.insert(hab);
            }else{
                HotArticleBrowsed updateHab = new HotArticleBrowsed();
                updateHab.setArticleId(articleId);
                String browserIds = habExist.getBrowserIds();
                if(StringUtils.isBlank(browserIds)){
                    browserIds = loginIdStr;
                }else if(!browserIds.contains(loginIdStr)){
                    browserIds += ","+loginIdStr;
                }
                updateHab.setBrowserIds(browserIds);
                hotArticleBrowsedMapper.updateById(updateHab);
            }
        });
        return articleService.load(articleId);
    }

    @Override
    public Map<Long, BigDecimal> queryConsumption(List<Long> articleIdList) {
        LambdaQueryWrapper<THotArticleOrderItem> hotItemQuery = new LambdaQueryWrapper<>();
        hotItemQuery.in(THotArticleOrderItem::getArticleId, articleIdList);
        List<THotArticleOrderItem> itemList = tHotArticleOrderItemMapper.selectList(hotItemQuery);
        if(CollectionUtil.isEmpty(itemList)){
            return Map.of();
        }
        Map<Long, List<Long>> articleOrderMap = itemList.stream().collect(
                Collectors.groupingBy(THotArticleOrderItem::getArticleId,
                        Collectors.mapping(THotArticleOrderItem::getOrderId, Collectors.toList())));
        Set<Long> orderIdSet = itemList.stream().map(THotArticleOrderItem::getOrderId).collect(Collectors.toSet());
        LambdaQueryWrapper<TOrders> orderQuery = new LambdaQueryWrapper<>();
        orderQuery.in(TOrders::getId, orderIdSet);
        orderQuery.eq(TOrders::getPaymentState, PaymentStateEnum.FINISHED.getCode());
        List<TOrders> orderList = tOrderMapper.selectList(orderQuery);
        Map<Long, BigDecimal> orderAmountMap = orderList.stream().collect(Collectors.toMap(TOrders::getId, TOrders::getAmount));
        Map<Long, BigDecimal> articleAmountMap = articleOrderMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
                e-> e.getValue().stream().map(orderAmountMap::get).reduce(BigDecimal.ZERO, BigDecimal::add)));
        return articleAmountMap;
    }

    @Override
    public PageVO<AdminConsumptionVo> consumptionPage(AdminConsumptionPageDto dto) {
        PageVO<AdminConsumptionVo> pageVO = new PageVO<>();
        Long articleId = dto.getArticleId();
        LambdaQueryWrapper<THotArticleOrderItem> hotItemQuery = new LambdaQueryWrapper<>();
        hotItemQuery.eq(THotArticleOrderItem::getArticleId, articleId);
        IPage<THotArticleOrderItem> itemPage = tHotArticleOrderItemMapper.selectPage(Page.of(dto.getCurrent(), dto.getPageSize()), hotItemQuery);
        List<THotArticleOrderItem> itemList = itemPage.getRecords();
        if(CollectionUtil.isEmpty(itemList)){
            return pageVO;
        }
        List<Long> orderIdList = itemList.stream().map(THotArticleOrderItem::getOrderId).collect(Collectors.toList());
        LambdaQueryWrapper<TOrders> orderQuery = new LambdaQueryWrapper<>();
        orderQuery.in(TOrders::getId, orderIdList);
        List<TOrders> orderList = tOrderMapper.selectList(orderQuery);
        if(CollectionUtil.isEmpty(orderList)){
            return pageVO;
        }
        Set<Long> customerIdSet = orderList.stream().map(TOrders::getCustomerId).collect(Collectors.toSet());
        List<AppCustomer> appCustomerList = appCustomerMapper.selectBatchIds(customerIdSet);
        if(CollectionUtil.isEmpty(appCustomerList)){
            return pageVO;
        }
        Map<Long, AppCustomer> customerMap = appCustomerList.stream().collect(Collectors.toMap(AppCustomer::getId, Function.identity()));
        List<AdminConsumptionVo> adminConsumptionVoList = orderList.stream().map(o->{
            AdminConsumptionVo adminConsumptionVo = new AdminConsumptionVo();
            adminConsumptionVo.setCustomerId(o.getCustomerId());
            adminConsumptionVo.setHotOrderId(o.getId());
            adminConsumptionVo.setConsumption(o.getAmount());
            adminConsumptionVo.setCreatedAt(o.getCreatedAt());
            AppCustomer appCustomer = customerMap.get(o.getCustomerId());
            if(appCustomer!=null){
                String nickname = appCustomer.getNickname();
                adminConsumptionVo.setNickname(nickname);
            }
            return adminConsumptionVo;
        }).collect(Collectors.toList());

        pageVO.setTotal(itemPage.getTotal());
        pageVO.setList(adminConsumptionVoList);
        return pageVO;
    }
}
